﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace ShenGu.Script
{
    public abstract class BaseScriptException : Exception
    {
        private int charIndex;
        private int lineIndex, columnIndex;

        protected BaseScriptException(int charIndex, string errorMessage, Exception innerException) : base(errorMessage, innerException)
        {
            this.charIndex = charIndex;
            this.lineIndex = this.columnIndex = -1;
        }
        protected BaseScriptException(SerializationInfo info, StreamingContext context) : base(info, context) { }

        private void CheckLine()
        {
            string script = Script;
            if (script != null && lineIndex < 0 && charIndex >= 0)
                ScriptParser.CheckLine(script, charIndex, out lineIndex, out columnIndex);
        }

        public abstract string Script { get; }
        public int CharIndex { get { return charIndex; } }
        public int LineIndex { get { CheckLine(); return lineIndex; } }
        public int ColumnIndex { get { CheckLine(); return columnIndex; } }
    }

    public abstract class ScriptException : BaseScriptException
    {
        private ScriptParser parser;
        
        protected ScriptException(ScriptParser parser, int charIndex, string errorMessage, Exception innerException) : base(charIndex, errorMessage, innerException)
        {
            this.parser = parser;
        }
        protected ScriptException(SerializationInfo info, StreamingContext context) : base(info, context) { }

        public ScriptParser Parser { get { return parser; } }
        public override string Script { get { return parser != null ? parser.Script : null; } }
    }

    public class ScriptParseException : ScriptException
    {
        internal ScriptParseException(ScriptParser parser, int charIndex, string errorMessage) : base(parser, charIndex, errorMessage, null) { }

        protected ScriptParseException(SerializationInfo info, StreamingContext context) : base(info, context) { }
    }

    public interface IScriptException
    {
        IScriptObject Value { get; }
    }

    public class ScriptExecuteException : ScriptException, IScriptException
    {
        private IScriptObject value;

        internal ScriptExecuteException(ScriptParser parser, int charIndex, string message, Exception innerException)
            : base(parser, charIndex, message, innerException) { }
        
        protected ScriptExecuteException(SerializationInfo info, StreamingContext context) : base(info, context) { }

        public IScriptObject Value
        {
            get
            {
                if (value == null)
                    value = ScriptString.Create(this.Message);
                return value;
            }
        }

        private static ScriptExecuteException Create(ScriptContext context, IScriptObject value, string message, Exception innerException)
        {
            ScriptExecuteException result;
            if (context == null || context.CurrentContext == null || context.CurrentContext.Current == null)
                result = new ScriptExecuteException(null, -1, message, innerException);
            else
            {
                ElementBase elem = context.CurrentContext.Current;
                result = new ScriptExecuteException(elem.Parser, elem.CharIndex, message, innerException);
            }
            result.value = value;
            return result;
        }

        public static ScriptExecuteException Create(ScriptContext context, IScriptObject value) { return Create(context, value, value != null ? value.ToValueString(context) : null, null); }

        public static ScriptExecuteException Create(ScriptContext context, Exception error) { return Create(context, new ScriptExceptionInstance(error), error.Message, error); }

        public static ScriptExecuteException Create(ScriptContext context, string message, Exception innerException) { return Create(context, null, message, innerException); }

        public static ScriptExecuteException Create(ScriptContext context, string message) { return Create(context, null, message, null); }

        public static void Throw(ScriptContext context, IScriptObject value) { throw Create(context, value); }

        public static void Throw(ScriptContext context, Exception error) { throw Create(context, error); }

        public static void Throw(ScriptContext context, string message) { throw Create(context, message, null); }

        public static void Throw(ScriptContext context, string message, Exception innerException) { throw Create(context, message, innerException); }
    }

    class ScriptExceptionInstance : ScriptObjectBase
    {
        private Exception instance;

        public ScriptExceptionInstance(Exception instance)
        {
            this.instance = instance;
        }

        public override object ToValue(ScriptContext context)
        {
            return instance;
        }

        public override string ToValueString(ScriptContext context)
        {
            Exception ex = instance;
            while (ex is System.Reflection.TargetInvocationException && ex.InnerException != null)
                ex = ex.InnerException;
            return ex.Message;
        }
    }
}
